wayland: Add support for xdg-output
authorOlivier Fourdan <ofourdan@redhat.com>
Mon, 15 Apr 2019 14:42:45 +0000 (16:42 +0200)
committerOlivier Fourdan <ofourdan@redhat.com>
Tue, 16 Apr 2019 14:14:43 +0000 (16:14 +0200)
Previously, the GDK backend for Wayland would deduce the logical size
of the monitors from the wl_output size and scale.

With the addition of fractional scaling which advertises a larger scale
value and then scale down the client surface, the computed logical size
of the monitors in GDK would be wrong and confuse applications which
insist on using the monitor size and position (like Firefox).

The xdg-output protocol aims at describing outputs in a way which is more
in line with the concept of an output on desktop oriented systems by
presenting the outputs using their logical size and position appropriately
transformed.

Add support for the optional xdg-output protocol so that the size and
position of the monitors as reported by GDK is correct even when using
fractional scaling.

Fixes: https://gitlab.gnome.org/GNOME/gtk/issues/1828
gdk/wayland/gdkdisplay-wayland.c
gdk/wayland/gdkdisplay-wayland.h
gdk/wayland/gdkmonitor-wayland.h
gdk/wayland/meson.build

index 777b90b7ef184a07f49d371e4d7d0121c65ac5d2..b4e51d6c63c0e21abece069d419e57a5a8b7e67a 100644 (file)
@@ -396,6 +396,8 @@ static void gdk_wayland_display_add_output        (GdkWaylandDisplay *display_wa
                                                    guint32            version);
 static void gdk_wayland_display_remove_output     (GdkWaylandDisplay *display_wayland,
                                                    guint32            id);
+static void gdk_wayland_display_init_xdg_output   (GdkWaylandDisplay *display_wayland);
+static void gdk_wayland_display_get_xdg_output    (GdkWaylandMonitor *monitor);
 
 static void
 gdk_registry_handle_global (void               *data,
@@ -527,6 +529,13 @@ gdk_registry_handle_global (void               *data,
                                                            &server_decoration_listener,
                                                            display_wayland);
     }
+  else if (strcmp(interface, "zxdg_output_manager_v1") == 0)
+    {
+      display_wayland->xdg_output_manager =
+            wl_registry_bind (registry, id, &zxdg_output_manager_v1_interface, 1);
+      gdk_wayland_display_init_xdg_output (display_wayland);
+      _gdk_wayland_display_async_roundtrip (display_wayland);
+    }
 
   g_hash_table_insert (display_wayland->known_globals,
                        GUINT_TO_POINTER (id), g_strdup (interface));
@@ -2170,6 +2179,123 @@ update_scale (GdkDisplay *display)
   g_list_free (seats);
 }
 
+static void
+gdk_wayland_display_init_xdg_output (GdkWaylandDisplay *display_wayland)
+{
+  int i;
+
+  GDK_NOTE (MISC,
+            g_message ("init xdg-output support, %d monitor(s) already present",
+                       display_wayland->monitors->len));
+
+  for (i = 0; i < display_wayland->monitors->len; i++)
+    gdk_wayland_display_get_xdg_output (display_wayland->monitors->pdata[i]);
+}
+
+static gboolean
+display_has_xdg_output_support (GdkWaylandDisplay *display_wayland)
+{
+  return (display_wayland->xdg_output_manager != NULL);
+}
+
+static gboolean
+monitor_has_xdg_output (GdkWaylandMonitor *monitor)
+{
+  return (monitor->xdg_output != NULL);
+}
+
+static gboolean
+should_update_monitor (GdkWaylandMonitor *monitor)
+{
+  return (GDK_MONITOR (monitor)->geometry.width != 0 &&
+          monitor->version < OUTPUT_VERSION_WITH_DONE);
+}
+
+static void
+apply_monitor_change (GdkWaylandMonitor *monitor)
+{
+  GDK_NOTE (MISC,
+            g_message ("monitor %d changed position %d %d, size %d %d",
+                       monitor->id,
+                       monitor->x, monitor->y,
+                       monitor->width, monitor->height));
+
+  gdk_monitor_set_position (GDK_MONITOR (monitor), monitor->x, monitor->y);
+  gdk_monitor_set_size (GDK_MONITOR (monitor), monitor->width, monitor->height);
+  monitor->wl_output_done = FALSE;
+  monitor->xdg_output_done = FALSE;
+
+  update_scale (GDK_MONITOR (monitor)->display);
+}
+
+static void
+xdg_output_handle_logical_position (void                  *data,
+                                    struct zxdg_output_v1 *xdg_output,
+                                    int32_t                x,
+                                    int32_t                y)
+{
+  GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
+
+  GDK_NOTE (MISC,
+            g_message ("handle logical position xdg-output %d, position %d %d",
+                       monitor->id, x, y));
+  monitor->x = x;
+  monitor->y = y;
+}
+
+static void
+xdg_output_handle_logical_size (void                  *data,
+                                struct zxdg_output_v1 *xdg_output,
+                                int32_t                width,
+                                int32_t                height)
+{
+  GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
+
+  GDK_NOTE (MISC,
+            g_message ("handle logical size xdg-output %d, size %d %d",
+                       monitor->id, width, height));
+  monitor->width = width;
+  monitor->height = height;
+}
+
+static void
+xdg_output_handle_done (void                  *data,
+                        struct zxdg_output_v1 *xdg_output)
+{
+  GdkWaylandMonitor *monitor = (GdkWaylandMonitor *) data;
+
+  GDK_NOTE (MISC,
+            g_message ("handle done xdg-output %d", monitor->id));
+
+  monitor->xdg_output_done = TRUE;
+  if (monitor->wl_output_done)
+    apply_monitor_change (monitor);
+}
+
+static const struct zxdg_output_v1_listener xdg_output_listener = {
+    xdg_output_handle_logical_position,
+    xdg_output_handle_logical_size,
+    xdg_output_handle_done,
+};
+
+static void
+gdk_wayland_display_get_xdg_output (GdkWaylandMonitor *monitor)
+{
+  GdkDisplay *display = GDK_MONITOR (monitor)->display;
+  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
+
+  GDK_NOTE (MISC,
+            g_message ("get xdg-output for monitor %d", monitor->id));
+
+  monitor->xdg_output =
+    zxdg_output_manager_v1_get_xdg_output (display_wayland->xdg_output_manager,
+                                           monitor->output);
+
+  zxdg_output_v1_add_listener (monitor->xdg_output,
+                               &xdg_output_listener,
+                               monitor);
+}
+
 static void
 output_handle_geometry (void             *data,
                         struct wl_output *wl_output,
@@ -2188,13 +2314,17 @@ output_handle_geometry (void             *data,
             g_message ("handle geometry output %d, position %d %d, phys. size %d %d, subpixel layout %s, manufacturer %s, model %s, transform %s",
                        monitor->id, x, y, physical_width, physical_height, subpixel_to_string (subpixel), make, model, transform_to_string (transform)));
 
-  gdk_monitor_set_position (GDK_MONITOR (monitor), x, y);
+  monitor->x = x;
+  monitor->y = y;
   gdk_monitor_set_physical_size (GDK_MONITOR (monitor), physical_width, physical_height);
   gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), subpixel);
   gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), make);
   gdk_monitor_set_model (GDK_MONITOR (monitor), model);
 
-  if (GDK_MONITOR (monitor)->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
+  if (should_update_monitor (monitor) || !monitor_has_xdg_output (monitor))
+    apply_monitor_change (monitor);
+
+  if (should_update_monitor (monitor))
     update_scale (GDK_MONITOR (monitor)->display);
 }
 
@@ -2203,19 +2333,14 @@ output_handle_done (void             *data,
                     struct wl_output *wl_output)
 {
   GdkWaylandMonitor *monitor = (GdkWaylandMonitor *)data;
-  GdkDisplay *display = gdk_monitor_get_display (GDK_MONITOR (monitor));
 
   GDK_NOTE (MISC,
             g_message ("handle done output %d", monitor->id));
 
-  if (!monitor->added)
-    {
-      monitor->added = TRUE;
-      g_ptr_array_add (GDK_WAYLAND_DISPLAY (display)->monitors, monitor);
-      gdk_display_monitor_added (display, GDK_MONITOR (monitor));
-    }
+  monitor->wl_output_done = TRUE;
 
-  update_scale (display);
+  if (!monitor_has_xdg_output (monitor) || monitor->xdg_output_done)
+    apply_monitor_change (monitor);
 }
 
 static void
@@ -2232,6 +2357,9 @@ output_handle_scale (void             *data,
   GDK_NOTE (MISC,
             g_message ("handle scale output %d, scale %d", monitor->id, scale));
 
+  if (monitor_has_xdg_output (monitor))
+    return;
+
   gdk_monitor_get_geometry (GDK_MONITOR (monitor), &previous_geometry);
   previous_scale = gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
 
@@ -2239,10 +2367,11 @@ output_handle_scale (void             *data,
   height = previous_geometry.height * previous_scale;
 
   gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale);
-  gdk_monitor_set_size (GDK_MONITOR (monitor), width / scale, height / scale);
+  monitor->width = width / scale;
+  monitor->height = height / scale;
 
-  if (GDK_MONITOR (monitor)->geometry.width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
-    update_scale (GDK_MONITOR (monitor)->display);
+  if (should_update_monitor (monitor))
+    apply_monitor_change (monitor);
 }
 
 static void
@@ -2264,11 +2393,12 @@ output_handle_mode (void             *data,
     return;
 
   scale = gdk_monitor_get_scale_factor (GDK_MONITOR (monitor));
-  gdk_monitor_set_size (GDK_MONITOR (monitor), width / scale, height / scale);
+  monitor->width = width / scale;
+  monitor->height = height / scale;
   gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh);
 
-  if (width != 0 && monitor->version < OUTPUT_VERSION_WITH_DONE)
-    update_scale (GDK_MONITOR (monitor)->display);
+  if (should_update_monitor (monitor) || !monitor_has_xdg_output (monitor))
+    apply_monitor_change (monitor);
 }
 
 static const struct wl_output_listener output_listener =
@@ -2295,13 +2425,17 @@ gdk_wayland_display_add_output (GdkWaylandDisplay *display_wayland,
   monitor->output = output;
   monitor->version = version;
 
-  if (monitor->version < OUTPUT_VERSION_WITH_DONE)
-    {
-      g_ptr_array_add (display_wayland->monitors, monitor);
-      gdk_display_monitor_added (GDK_DISPLAY (display_wayland), GDK_MONITOR (monitor));
-    }
+  g_ptr_array_add (display_wayland->monitors, monitor);
+  gdk_display_monitor_added (GDK_DISPLAY (display_wayland), GDK_MONITOR (monitor));
 
   wl_output_add_listener (output, &output_listener, monitor);
+
+  GDK_NOTE (MISC,
+            g_message ("xdg_output_manager %p",
+                       display_wayland->xdg_output_manager));
+
+  if (display_has_xdg_output_support (display_wayland))
+    gdk_wayland_display_get_xdg_output (monitor);
 }
 
 struct wl_output *
index 8f06188841041c17c9115531993b96d2a7474321..6c5265f6a4b869cf5552d7286c2c01ac616cd621 100644 (file)
@@ -35,6 +35,7 @@
 #include <gdk/wayland/xdg-foreign-unstable-v1-client-protocol.h>
 #include <gdk/wayland/keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h>
 #include <gdk/wayland/server-decoration-client-protocol.h>
+#include <gdk/wayland/xdg-output-unstable-v1-client-protocol.h>
 
 #include <glib.h>
 #include <gdk/gdkkeys.h>
@@ -109,6 +110,7 @@ struct _GdkWaylandDisplay
   struct zxdg_importer_v1 *xdg_importer;
   struct zwp_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit;
   struct org_kde_kwin_server_decoration_manager *server_decoration_manager;
+  struct zxdg_output_manager_v1 *xdg_output_manager;
 
   GList *async_roundtrips;
 
index 581908c513ca94b5df8d07eed402ddebf3e86183..593ab1d85c378649f387833c4fcb424461d6772a 100644 (file)
@@ -30,6 +30,15 @@ struct _GdkWaylandMonitor {
   guint32 version;
   struct wl_output *output;
   gboolean added;
+
+  struct zxdg_output_v1 *xdg_output;
+  /* Size and position, can be either from wl_output or xdg_output */
+  int32_t x;
+  int32_t y;
+  int32_t width;
+  int32_t height;
+  gboolean wl_output_done;
+  gboolean xdg_output_done;
 };
 
 struct _GdkWaylandMonitorClass {
index 0d0cf8085113fbac6a15d9ced8bceb4336255f38..40f08bdd885428ef702e0997eeb44ba950bcfaa4 100644 (file)
@@ -58,6 +58,7 @@ proto_sources = [
   ['tablet', 'unstable', 'v2', ],
   ['keyboard-shortcuts-inhibit', 'unstable', 'v1', ],
   ['server-decoration', 'private' ],
+  ['xdg-output', 'unstable', 'v1', ],
 ]
 
 gdk_wayland_gen_headers = []